<!-- 自定义滑动标签组件 -->
<template>
  <view class="y-tabs">
    <view class="y-tabs-wrapper">
      <view class="y-tabs-wrapper-scroll">
        <scroll-view
          v-if="list.length > 0"
          id="scrollContainer"
          class="scroll-h"
          :scroll-x="scroll"
          :scroll-left="scroll ? scrollLeft : 0"
          :scroll-with-animation="scroll"
        >
          <view class="y-tab-box">
            <block v-for="(item, index) in list" :key="index">
              <view class="y-tab-item" @tap="clickHandler(index)">
                <view class="y-tab-first-text"> {{ item.title }} </view>
                <view class="y-tab-first-two"> {{ item.subtitle }} </view>
              </view>
            </block>
            <view
              class="y-tab-text-line"
              :style="[
                {
                  width: `${lineWidth}rpx`,
                  transform: `translate(${lineLeft}px)`,
                  transitionDuration: `${firstTime ? 0 : 300}ms`,
                },
              ]"
            ></view>
          </view>
        </scroll-view>
      </view>
      <view class="slot-style"> <slot name="right"></slot></view>
    </view>
  </view>
</template>

<script>
//这里可以导入其他文件（比如：组件，工具js，第三方插件js，json文件，图片文件等等）
//例如：import 《组件名称》 from '《组件路径》';

export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  props: {
    /**
     * 是否滚动
     */
    scroll: {
      type: [Boolean, String],
      default: true,
    },

    //滑块长度
    lineWidth: {
      type: [Number, String],
      default: 60,
    },

    current: {
      type: [Number, String],
      default: 0,
    },

    // tabs标签数组
    list: {
      type: Array,
      default: [
        {
          title: "主标题",
          subtitle: "副标题",
        },
      ],
    },
  },
  data() {
    //这里存放数据
    return {
      firstTime: true,
      currentWidth: 0, // 当前选项的宽度
      scrollLeft: 0, // 距离左边的位置
      lineLeft: 0, // 滑块距离左侧的位置
      containerWidth: 0, // 容器的宽度
      tabsRect: {
        left: 0,
      },
    };
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {
    current: {
      immediate: true,
      handler(newValue, oldValue) {
        // 内外部值不相等时，才尝试移动滑块
        this.$nextTick(() => {
          this.resize();
        });
      },
    },
    // list变化时，重新渲染list各项信息
    list() {
      this.$nextTick(() => {
        this.resize();
      });
    },
  },
  //方法集合
  methods: {
    clickHandler(index) {
      this.$emit("tabTap", index);
    },

    init() {
      uni.$u.sleep().then(() => {
        this.resize();
      });
    },
    setLineLeft() {
      const tabItem = this.list[this.current];
      if (!tabItem) {
        return;
      }
      // 获取滑块该移动的位置
      let lineLeft = this.list
        .slice(0, this.current)
        .reduce((total, curr) => total + curr.rect.width, 0);
      this.lineLeft = lineLeft + (tabItem.rect.width - this.lineWidth) / 2;

      // 如果是第一次执行此方法，让滑块在初始化时，瞬间滑动到第一个tab item的中间
      // 这里需要一个定时器，因为在非nvue下，是直接通过style绑定过渡时间，需要等其过渡完成后，再设置为false(非第一次移动滑块)
      if (this.firstTime) {
        setTimeout(() => {
          this.firstTime = false;
        }, 10);
      }
    },

    setScrollLeft() {
      // 当前活动tab的布局信息，有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
      const tabRect = this.list[this.current];
      // 累加得到当前item到左边的距离
      const offsetLeft = this.list
        .slice(0, this.current)
        .reduce((total, curr) => {
          return total + curr.rect.width;
        }, 0);
      // 此处为屏幕宽度
      const windowWidth = uni.$u.sys().windowWidth;
      // 将活动的tabs-item移动到屏幕正中间，实际上是对scroll-view的移动
      let scrollLeft =
        offsetLeft -
        (this.tabsRect.width - tabRect.rect.width) / 2 -
        (windowWidth - this.tabsRect.right) / 2 +
        this.tabsRect.left / 2;
      // 这里做一个限制，限制scrollLeft的最大值为整个scroll-view宽度减去tabs组件的宽度
      scrollLeft = Math.min(
        scrollLeft,
        this.containerWidth - this.tabsRect.width
      );
      this.scrollLeft = Math.max(0, scrollLeft);
    },

    resize() {
      // 如果不存在list，则不处理
      if (this.list.length === 0) {
        return;
      }
      Promise.all([this.getTabsRect(), this.getAllItemRect()]).then(
        ([tabsRect, itemRect = []]) => {
          this.tabsRect = tabsRect;
          this.containerWidth = 0;
          itemRect.map((item, index) => {
            // 计算scroll-view的宽度，这里
            this.containerWidth += item.width;
            // 另外计算每一个item的中心点X轴坐标
            this.list[index].rect = item;
          });
          // 获取了tabs的尺寸之后，设置滑块的位置
          this.setLineLeft();
          this.setScrollLeft();
        }
      );
    },

    // 获取容器的宽度
    getTabsRect() {
      return new Promise((resolve) => {
        this.queryRect("#scrollContainer").then((size) => resolve(size));
      });
    },
    // 获取所有标签的尺寸
    getAllItemRect() {
      return new Promise((resolve) => {
        const promiseAllArr = this.list.map((item, index) =>
          this.queryRect(".y-tab-item")
        );
        Promise.all(promiseAllArr).then((sizes) => resolve(sizes));
      });
    },

    // 获取各个标签的尺寸
    queryRect(el) {
      // $uGetRect为uView自带的节点查询简化方法，详见文档介绍：https://www.uviewui.com/js/getRect.html
      // 组件内部一般用this.$uGetRect，对外的为this.$u.getRect，二者功能一致，名称不同
      return new Promise((resolve) => {
        this.$uGetRect(`${el}`).then((size) => {
          resolve(size);
        });
      });
    },
  },
  //生命周期 - 创建完成（可以访问当前this实例）
  created() {},
  //生命周期 - 挂载完成（可以访问DOM元素）
  async mounted() {
    await this.init();
  },
};
</script>
<style lang="scss" scoped>
//@import url(); 引入公共css类
.y-tabs {
  .y-tabs-wrapper {
    display: flex;
    justify-content: space-between;
    .y-tabs-wrapper-scroll {
      .scroll-h {
        width: 600rpx;
        height: 90rpx;
        flex-direction: row;
      }
      .y-tab-box {
        width: 100%;
        height: 100%;
        display: flex;
        .y-tab-item {
          padding-right: 10px;
          .y-tab-first-text,
          .y-tab-first-two {
            text-align: center;
          }
          .y-tab-first-text {
            font-size: 28rpx;
            padding-bottom: 5rpx;
          }
          .y-tab-first-two {
            font-size: 24rpx;
            padding: 0 20rpx;
            color: #898989;
          }
        }

        .y-tab-text-line {
          height: 6rpx;
          background-color: #ffbf01;
          width: 60rpx;
          position: absolute;
          left: 18rpx;
          right: 18rpx;
          bottom: 40rpx;
          border-radius: 200rpx;
          transition-property: transform;
          transition-duration: 300ms;
          top: 80rpx;
        }
      }
    }
  }
}
</style>
